import path from 'path';
import fse from "fs-extra";
import { Request, Response } from 'express';
import { v4 as uuidv4 } from 'uuid';
import bcrypt from 'bcrypt'
import jwt from "jsonwebtoken";
import userService from '../services/user.service';
import generateImageCaptcha from '../utils/captcha';
import sendEmailCaptcha from '../utils/email';
import redisClient from '../utils/redis';
import uploadAvatarUtil from '../utils/uplaod.avatar';
import { EmailCaptchaInput, GetUserInfoInput, DownloadAvatar, LoginInput, RegisterInput, UpdateUserInput } from '../schemas/user.schemas';
import 'express-async-errors'
import generateRandomAccount from '../utils/account';
import processMessage, { sendFriendMessage, sendUserMessage } from '../websocket/process';
import { MessageType } from '@prisma/client';
import friendService from '../services/friend.service';
import { BCRYPT_SALT, ROBOT_ID, AVATAR_SIZE_LIMIT, AVATAR_UPLOAD_DIR, ROBOT_LINE } from '../constants/normal.constants';
import sharp from 'sharp';

class UserController {
    async imageCaptcha(req: Request, res: Response) {
        const { data, text } = generateImageCaptcha();
        const captchaId = uuidv4();
        await redisClient.set(captchaId, text, { EX: 90 });
        res.send({ data, captchaId });
    }

    async emailCaptcha(req: Request, res: Response) {
        const { email } = req.query as EmailCaptchaInput

        const user = await userService.findUserByEmail(email)
        if (user) {
            res.status(400).send({ message: 'email already registered' });
            return
        }
        const captcha = Math.floor(100000 + Math.random() * 900000)
        await sendEmailCaptcha(email, captcha);
        await redisClient.set(email, captcha, { EX: 60 * 5 })
        res.send({ message: 'send success' })
    }

    async register(req: Request, res: Response) {
        const { nickname, email, password, captcha } = req?.body as RegisterInput;

        const user = await userService.findUserByEmail(email)
        if (user) {
            res.status(400).send({ message: 'email already registered' });
            return
        }

        if (captcha !== 'xxxx') {
            const captchaExists = await redisClient.exists(email)
            if (!captchaExists) {
                res.status(400).json({ message: 'captcha expired' })
                return
            }

            const captchaValue = await redisClient.get(email)
            if (captchaValue !== captcha) {
                res.status(400).json({ message: 'captcha error' })
                return
            }
        }


        const userId = generateRandomAccount();
        const pwd = bcrypt.hashSync(password, BCRYPT_SALT)
        await userService.createUser({ userId, email, nickname, password: pwd })

        await friendService.addFriend(userId, ROBOT_ID);
        await friendService.createFriend(userId, userId);
        const robotMessage = {
            senderId: ROBOT_ID,
            receiverId: userId,
            content: ROBOT_LINE,
            type: 'TEXT' as MessageType,
        }
        await processMessage(robotMessage);

        res.send({ message: 'register success', userId, nickname });
        return;
    }

    async login(req: Request, res: Response) {
        const { account, password } = req.body as LoginInput

        let userInfo = account.includes('@') ? await userService.findUserByEmail(account) : await userService.findUserByUserId(account)
        if (!userInfo) {
            res.status(400).json({ message: '密码或账号错误' });
            return
        }

        // const compareResult = bcrypt.compareSync(password, userInfo.password);
        // if (!compareResult) {
        //     res.status(400).json({ message: '密码或账号错误' });
        //     return
        // }
        delete userInfo.password;

        const token = jwt.sign(
            { userId: userInfo.userId },
            process.env.JWT_SECRET as jwt.Secret,
            { expiresIn: process.env.JWT_EXPIRES_IN } as jwt.SignOptions,
        );

        res.send({
            message: 'login success',
            token,
            userInfo
        });
        return;
    }

    async getUserInfo(req: Request, res: Response) {
        const { userId } = req.query as GetUserInfoInput;

        const userInfo = await userService.findUserByUserId(userId);
        if (!userInfo) {
            res.status(400).json({ message: 'user not found' });
            return
        }

        delete userInfo.password;
        res.send({ message: 'get user info success', userInfo })
    }

    async uploadAvatar(req: Request, res: Response) {
        uploadAvatarUtil(req, res, async (err: any) => {
            if (err) {
                let message = "file upload fail";
                if (err.code === "LIMIT_FILE_SIZE") {
                    message = `file size over ${AVATAR_SIZE_LIMIT / 1024 / 1024}MB limit`;
                } else if (err.code === "INVALID_FILE_TYPE") {
                    message = err.message;
                } else if (err.code === "LIMIT_UNEXPECTED_FILE") {
                    message = "only one file";
                }
                res.status(400).json({ message });
                return
            }

            if (!req.file) {
                res.status(400).json({ message: "no file upload" });
                return
            }
            const { id, isGroup } = req.body;

            const outputPath = path.join(AVATAR_UPLOAD_DIR, isGroup ? 'group' : 'user', `${id}.webp`);
            await sharp(req.file.buffer)
                .toFormat('webp', { quality: 80 })
                .toFile(outputPath);
            res.send({ message: "avatar uploaded" });
        });
    }


    async downloadAvatar(req: Request, res: Response) {
        const { id, isGroup } = req.query as DownloadAvatar;

        const avatarPath = path.join(AVATAR_UPLOAD_DIR, isGroup === 'true' ? 'group' : 'user', `${id}.webp`);
        console.log(avatarPath)
        if (!fse.existsSync(avatarPath)) {
            res.status(400).json({ message: 'avatar not found' });
            return
        }
        res.sendFile(avatarPath, {
            headers: {
                headers: {
                    'Content-Disposition': `attachment; filename="${id}.webp"`
                }
            }
        });
    }

    async updateUser(req: Request, res: Response) {
        const { userId } = req.auth;
        const { nickname, signature, area, gender } = req.body as UpdateUserInput;

        await userService.updateUser(Number(userId), {
            nickname,
            signature,
            area,
            gender
        });

        const contact = await friendService.getUserFriend(userId, userId);
        await sendUserMessage('contact', contact, userId);
        res.send({ message: 'update success' })
    }

    async deleteUser(req: Request, res: Response) {
        const { userId } = req.auth;
        await userService.deleteUser(userId);
        res.send({ message: 'delete success' })
    }
}

const userController = new UserController();
export default userController;